Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execution tracing: GraphQL query to get storage inputs for past blocks #2491

Draft
wants to merge 34 commits into
base: master
Choose a base branch
from

Conversation

Dentosal
Copy link
Member

@Dentosal Dentosal commented Dec 11, 2024

Description

To support local debugging and execution tracing, we need to add an API that returns the state that VM needs to execute transactions. This is done by recording all database accesses during tx validation, called storage read replay (bikeshedding in progress). This data is then exposed as-is through GraphQL. The API is non-trivial to consume, but a seperate client is proviced, see https://github.com/FuelLabs/execution-trace

The implementation exposes database table names and representations directly. Maintaining backwards compatibility with this could turn out to be quite hard.

Follow-ups:

Example query:

mutation {
  storageReadReplay(height:"4") { column, key, value }
}

TODO

Open questions:

  • Do we really want --debug flag to enabled this, as opposed to super-high query cost or a separate flag to enable?
  • What should the query cost be?

Checklist

  • Breaking changes are clearly marked as such in the PR description and changelog: No breaking changes!
  • New behavior is reflected in tests
  • The specification matches the implemented behavior: This is outside spec scope.

Before requesting review

  • I have reviewed the code myself
  • I have created follow-up issues caused by this PR and linked them here

After merging, notify other teams

See the VM PR FuelLabs/fuel-vm#881.

@Dentosal Dentosal added enhancement New feature or request fuel-block-executor wasm WASM-based block execution labels Dec 11, 2024
@Dentosal Dentosal self-assigned this Dec 11, 2024
@Dentosal Dentosal force-pushed the dento/execution-trace branch from 8d80d7b to 3ba2bc7 Compare December 19, 2024 15:18
@Dentosal Dentosal changed the title Execution tracing for past blocks Execution tracing: add GraphQL endpoint to get storage inputs for past blocks Jan 15, 2025
@Dentosal Dentosal changed the title Execution tracing: add GraphQL endpoint to get storage inputs for past blocks Execution tracing: GraphQL query to get storage inputs for past blocks Jan 15, 2025
Dentosal added a commit to FuelLabs/fuels-rs that referenced this pull request Jan 17, 2025
- Related to #1432

# Release notes

In this release, we:

- Changed `ABIDecoder` methods to take `std::io::Read` instead of
`&[u8]`, allowing it to be used in a streaming manner.

# Summary

`ABIDecoder` methods take `bytes: impl std::io::Read` instead of `bytes:
&[u8]`. This allows decoding abi types without having to know the size
in advance. This is particularly useful when reading them directly from
VM memory, which will be used by the indexer after
FuelLabs/fuel-core#2491 is done.

# Breaking Changes

`ABIDecoder` methods take `bytes: impl std::io::Read` instead of `bytes:
&[u8]`. Callers using arrays or `Vec` must change the argument from
`&value` to `value.as_slice()`.

# Checklist

- [x] All **changes** are **covered** by **tests** (or not applicable)
- [x] All **changes** are **documented** (or not applicable)
- [x] I **reviewed** the **entire PR** myself (preferably, on GH UI)
- [x] I **described** all **Breaking Changes** (or there's none)

---------

Co-authored-by: hal3e <[email protected]>
Co-authored-by: Ahmed Sagdati <[email protected]>
Co-authored-by: segfault-magnet <[email protected]>
@Dentosal Dentosal marked this pull request as ready for review January 24, 2025 23:43
@Dentosal Dentosal requested a review from a team January 24, 2025 23:43
@AurelienFT AurelienFT self-requested a review January 27, 2025 16:46
Copy link
Contributor

@AurelienFT AurelienFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good integration with database types ! Liked it !

ctx: &Context<'_>,
height: U32,
) -> async_graphql::Result<Vec<StorageReadReplayEvent>> {
let config = ctx.data_unchecked::<GraphQLConfig>().clone();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let config = ctx.data_unchecked::<GraphQLConfig>().clone();
let config = ctx.data_unchecked::<GraphQLConfig>();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -702,6 +702,114 @@ where
Ok(())
}

#[tracing::instrument(skip_all)]
fn record_storage_reads_for<D>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is never called in the code. Isn't it supposed to be called here ? Or it's outdated ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hahahaha I already managed to refactor that code out. Nice. I'll remove it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Dentosal Dentosal requested review from a team and AurelienFT January 29, 2025 15:02
AurelienFT
AurelienFT previously approved these changes Jan 30, 2025
Copy link
Contributor

@AurelienFT AurelienFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I would vouch for an indepedent flag that can be enabled by default when we use debug but that could be enabled by himself also.

Copy link
Member

@MitchTurner MitchTurner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've read a lot more, but I'm still WIP with my review. Will add more feedback.

Comment on lines 115 to 121
/// Query costs for generating execution trace for a block.®
#[clap(
long = "query-cost-storage-read-replay",
default_value = DEFAULT_QUERY_COSTS.storage_read_replay.to_string(),
env
)]
pub storage_read_replay: usize,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verbiage around this isn't clear to me.

This argument is for setting the costs? I think more context is required here and perhaps a more specific name for the field.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It's a filed in a struct called QueryCosts, and all other arguments have similar purpose.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ah ah. I remember this now. I suggested on another PR that we should rename this to "Complexity" or something (just because we already have concepts of gas costs etc). I won't nitpick too much on this since at least it's within the graphql crate, but giving some clarity in the comment on what types of costs this is would be valuable (even if the other args aren't that clear).

bin/fuel-core/src/cli/run/graphql.rs Outdated Show resolved Hide resolved
Copy link
Member

@MitchTurner MitchTurner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good overall. I add a few easy-to-fix comments.

One thing I'd like to call out is there doesn't seem to be any UTs for the new behavior. I feel like they would be relatively easy to add and would push things left a bit.

tests/tests/storage_read_replay.rs Outdated Show resolved Hide resolved
/// Create a counter contract.
/// Increment it multiple times, and make sure the replay gives correct storage state every time.
#[tokio::test(flavor = "multi_thread")]
async fn test_storage_read_replay_returns_counter_state() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like tests like this a lot. It's easy to read since the helpers make_counter_contract and increment_counter are very declarative.

It also gives an example of how the chain can be used as you're actually deploying something and interacting with it. Eventually, I want a whole testing framework like this for deploying Sway contracts inside of tests--not for integ tests, but for dApp tests.

tests/tests/storage_read_replay.rs Outdated Show resolved Hide resolved
crates/client/src/client.rs Outdated Show resolved Hide resolved
tests/tests/storage_read_replay.rs Outdated Show resolved Hide resolved
crates/types/Cargo.toml Outdated Show resolved Hide resolved
use fuel_core_types::fuel_types::BlockHeight;

#[test]
fn execution_trace_block_tx_gql_output() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to specify that this is a compatibility regression/snapshot test. I don't get anything from this test name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have loads of *_gql_output tests that do this. Should those all be renamed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(changed the method name to match storage_read_replay in fc097bf, as that's what the feature is now called)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have loads of *_gql_output tests that do this. Should those all be renamed?

Not your responsibility. I don't think upholding the precedence gains us anything here. I'd prefer clear names wherever possible.

@Dentosal
Copy link
Member Author

Dentosal commented Feb 7, 2025

Taking this back to draft until #2661 is merged, as I want to refactor this on top of it.

@Dentosal Dentosal marked this pull request as draft February 7, 2025 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request fuel-block-executor wasm WASM-based block execution
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants